package abacab.queue;

import abacab.actor.Actor;
import abacab.queue.PriorityActorQueue.LevelActorQueue;
import abacab.queue.PriorityActorQueue.PriorityQueueLevel;

/**
 * Represents a queue of actors
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class LevelActorQueueImpl extends LevelActorQueue {
	/** The monitor for concurrent access */
	private final Object	monitor;

	/** The head */
	private Item					head;

	/** The tail */
	private Item					tail;

	/**
	 * Create an instance
	 * 
	 * @param level
	 *          the priority level interface
	 */
	public LevelActorQueueImpl(PriorityQueueLevel level) {
		super(level);
		this.monitor = new Object();
	}

	@Override
	public final boolean isEmpty() {
		return this.tail == null;
	}

	@Override
	public final int size() {
		int count = 0;
		synchronized (this.monitor) {
			for (Item e = this.head; e != null; e = e.next) {
				count++;
			}
		}
		return count;
	}

	@Override
	public <M> void offer(Actor<M> actor) {
		Item item = new Item(actor);
		synchronized (this.monitor) {
			if (this.tail == null) {
				this.head = this.tail = item;
				firstActor();
			}
			else {
				this.tail.next = item;
				this.tail = item;
			}
		}
	}

	@Override
	public <M> Actor<M> poll() {
		synchronized (this.monitor) {
			if (this.head != null) {
				Actor actor = this.head.actor;
				if (actor.isReadyForExecution()) {
					this.head = this.head.next;
					if (this.head == null) {
						this.tail = null;
						lastActor();
					}
					return actor;
				}
				Item prev = this.head;
				Item e = this.head.next;
				while (e != null) {
					actor = e.actor;
					if (actor.isReadyForExecution()) {
						prev.next = e.next;
						if (this.tail == e) {
							this.tail = prev;
						}
						return actor;
					}
					prev = e;
					e = e.next;
				}
			}
			else {
				lastActor();
			}
		}
		return null;
	}

	//
	//
	//
	//
	//
	//
	//
	//
	//
	//
	// ************************************************************************
	// ************************************************************************
	// AN ITEM IN THE QUEUE
	// ************************************************************************
	// ************************************************************************

	/**
	 * An item in the priority queue
	 */
	private static final class Item {
		/** The actor */
		final Actor	actor;

		/** The next item */
		Item				next;

		/**
		 * Create a queued item
		 * 
		 * @param actor
		 *          the actor
		 */
		Item(Actor actor) {
			this.actor = actor;
		}
	}
}